package com.chatopera.cc.service;

import com.chatopera.cc.basic.Constants;
import com.chatopera.cc.basic.MainContext;
import com.chatopera.cc.cache.Cache;
import com.chatopera.cc.exception.CSKefuException;
import com.chatopera.cc.model.AgentUser;
import com.chatopera.cc.model.Contacts;
import com.chatopera.cc.model.OnlineUser;
import com.chatopera.cc.model.User;
import com.chatopera.cc.persistence.repository.*;
import com.chatopera.cc.proxy.OnlineUserProxy;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.ui.ModelMap;

import javax.persistence.criteria.Predicate;
import java.util.*;

@Service
public class ContactsService {
    private final static Logger logger = LoggerFactory.getLogger(ContactsService.class);

    @Autowired
    private ContactsRepository contactsRes;
    @Autowired
    private Cache cache;

    @Autowired
    private AgentUserRepository agentUserRes;

    @Autowired
    private OnlineUserRepository onlineUserRes;

    @Autowired
    private SNSAccountRepository snsAccountRes;

    public static Specification<Contacts> buildSpecification(Contacts contacts, User user) {

        return (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("orgi"), user.getOrgi()));
            predicates.add(root.get("organ").in(contacts.getOrganSet()));
            /*CriteriaBuilder.In<String> in = builder.in(root.get("organ"));
            for (String organ : contacts.getOrganSet()) {
                in.value(organ);
            }

            predicates.add(builder.and(in));*/

            if (StringUtils.isNotEmpty(contacts.getCkind()))
                predicates.add(builder.equal(root.get("ckind"), contacts.getCkind()));
            predicates.add(builder.equal(root.get("datastatus"), contacts.isDatastatus()));
            if (contacts.getBegintime() != null)
                predicates.add(builder.greaterThanOrEqualTo(root.get("createtime"), contacts.getBegintime()));
            if (contacts.getEndtime() != null)
                predicates.add(builder.lessThan(root.get("createtime"), DateUtils.addDays(contacts.getEndtime(), 1)));
            if (StringUtils.isNotBlank(contacts.getCreater())) {
                predicates.add(builder.equal(root.get("creater"), contacts.getCreater()));
            } else {
                Predicate p1 = builder.equal(root.get("creater"), user.getId());
                Predicate p2 = builder.equal(root.get("shares"), user.getId());
                Predicate p3 = builder.equal(root.get("shares"), "all");
                predicates.add(builder.or(p1, p2, p3));
            }
            String q = contacts.getQ();
            if (StringUtils.isNotBlank(q)) {
                Predicate p1 = builder.like(root.get("name"), "%" + q + "%");
                Predicate p2 = builder.like(root.get("mobileno"), "%" + q + "%");
                predicates.add(builder.or(p1, p2));
            }

            Predicate[] arr = new Predicate[predicates.size()];
            predicates.toArray(arr);

            return builder.and(arr);
        };
    }

    public String searchForIndex(ModelMap map, User logined, Contacts contacts, Pageable page) {
        String ckind = contacts.getCkind();
        if (StringUtils.isNotEmpty(ckind)) {
            map.put("ckind", ckind);
        }

        Page<Contacts> contactsPage = this.find(contacts, logined, page);
        map.addAttribute("contactsList", contactsPage);
        map.addAttribute("approachable", this.findContactsApproachableData(contactsPage, logined));
        return "apps/contacts/index";
    }


    public Page<Contacts> find(Contacts contacts, User user, Pageable pageable) {
        Page<Contacts> contactsPage = contactsRes.findAll(buildSpecification(contacts, user), pageable);
        return contactsPage;
    }

    /**
     * 批量查询联系人的可触达状态
     *
     * @param contactsPage
     * @param user
     */
    public Set<String> findContactsApproachableData(Page<Contacts> contactsPage, User user) {
        Set<String> approachable = new HashSet<>();
        for (Contacts c : contactsPage.getContent()) {
            try {
                if (liveApproachChannelsByContactid(user, c, isSkypeSetup(user.getOrgi())).size() > 0) {
                    approachable.add(c.getId());
                }
            } catch (CSKefuException e) {
                logger.warn("[bindContactsApproachableData] error", e);
            }
        }

        return approachable;
    }

    /**
     * 检查Skype渠道是否被建立
     *
     * @return
     */
    public boolean isSkypeSetup(final String orgi) {
        if (MainContext.hasModule(Constants.CSKEFU_MODULE_SKYPE) && snsAccountRes.countBySnstypeAndOrgi(
                Constants.CSKEFU_MODULE_SKYPE, orgi) > 0) {
            return true;
        }
        return false;
    }

    /**
     * 根据联系人ID获得一个当前可以触达联系人的方式（们）
     * 检查时过滤掉和其它坐席聊天中的联系人
     *
     * @param logined 当前查询该信息的访客
     * @param
     * @return
     */
    public List<MainContext.ChannelType> liveApproachChannelsByContactid(
            final User logined,
            Contacts contacts,
            final boolean isCheckSkype) throws CSKefuException {
        logger.info("[liveApproachChannelsByContactid] contact id {}", contacts.getId());

        List<MainContext.ChannelType> result = new ArrayList<>();

//        Optional<Contacts> contactOpt = contactsRes.findById(contactid).filter(p -> !p.isDatastatus());

        if (!contacts.isDatastatus()) {
            // 查看 WebIM 渠道
            agentUserRes.findOneByContactIdAndStatusNotAndChannelAndOrgi(
                    contacts.getId(),
                    MainContext.AgentUserStatusEnum.END.toString(),
                    MainContext.ChannelType.WEBIM.toString(),
                    contacts.getOrgi())
                    .filter(p -> StringUtils.equals(p.getAgentno(), logined.getId()))
                    .ifPresent(p -> {
                        if (!cache.existBlackEntityByUserIdAndOrgi(p.getUserid(), logined.getOrgi())) {
                            // 访客在线 WebIM，排队或服务中
                            result.add(MainContext.ChannelType.WEBIM);
                        } else {
                            // 该访客被拉黑
                        }
                    });

            // 查看 Skype 渠道
            if (isCheckSkype && StringUtils.isNotBlank(
                    contacts.getSkypeid())) {
                // 查找匹配的OnlineUser
                Optional<OnlineUser> opt = onlineUserRes.findOneByContactidAndOrigAndChannel(
                        contacts.getId(),
                        logined.getOrgi(),
                        Constants.CSKEFU_MODULE_SKYPE);
                if (opt.isPresent()) {
                    // 联系人存在访客信息
                    // 并且该访客没有被拉黑
                    if (!cache.existBlackEntityByUserIdAndOrgi(
                            opt.get().getId(), logined.getOrgi())) {
                        Optional<AgentUser> agentUserOpt = cache.findOneAgentUserByUserIdAndOrgi(
                                opt.get().getId(), logined.getOrgi());
                        if (agentUserOpt.isPresent()) {
                            AgentUser agentUser = agentUserOpt.get();
                            if ((StringUtils.equals(
                                    agentUser.getStatus(), MainContext.AgentUserStatusEnum.INSERVICE.toString())) &&
                                    (StringUtils.equals(agentUser.getAgentno(), logined.getId()))) {
                                // 该联系人的Skype账号被服务中
                                // TODO 此处可能是因为该联系的Skype对应的AgentUser没有被结束，长期被一个坐席占有
                                // 并不合理，后期需要加机制维护Skype的离线信息（1，Skype Agent查询;2, 加入最大空闲时间限制）
                                result.add(MainContext.ChannelType.SKYPE);
                            }
                        } else {
                            // 该联系人的Skype OnlineUser存在，而且未被其它坐席占用
                            // 并且该联系人没有被拉黑
                            result.add(MainContext.ChannelType.SKYPE);
                        }
                    }
                } else {
                    // 该联系人的Skype账号对应的OnlineUser不存在
                    // TODO 新建OnlineUser
                    OnlineUserProxy.createNewOnlineUserWithContactAndChannel(
                            contacts, logined, Constants.CSKEFU_MODULE_SKYPE);
                    result.add(MainContext.ChannelType.SKYPE);
                }
            }
        } else {
            // can not find contact, may is deleted.
            throw new CSKefuException("Contact does not available.");
        }

//        logger.info("[liveApproachChannelsByContactid] get available list {}", StringUtils.join(result, "|"));

        return result;
    }
}
